﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.Text.RegularExpressions;

namespace Serna
{
    public static partial class Serializers
    {




        /// <summary>
        /// 
        /// </summary>
        internal class InternalJSONSerializer
        {
            SerializationSettings ss = new SerializationSettings();

            public string Serialize(object obj)
            {
                //ss = settings;
                StringBuilder attribute = new StringBuilder();
                return SerializeObject(obj, attribute);
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            string SerializeObject(object obj, StringBuilder attribute)
            {
                //Must be supplied from the class


                // bool skipUnnecesarySymbols = false;

                var od = ObjectsFactory.GetObjectDefinition(obj);
                object ho = null;   //help object           
                object key = null;
                object val = null;

                StringBuilder sb = new StringBuilder();
                //attribute.Clear();
                
                string internalObject = "";
                StringBuilder myAttributes = null;
                bool notFirstProperty = true;
                                
                switch (od.Description)
                {
                    case ObjectDefinition.eObjectDescription.CustomClass:

                        sb.Append("{");

                        #region "CUSTOM CLASS"

                        foreach (var property in od.Properties)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;                                
                            else
                                sb.Append(",");
                                

                            //Writing key (key is property name (string))
                            WriteToString(sb, TypeString, property.Key, null, ss);

                            //here : can be skipped in short format for space economy
                            if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                sb.Append(":");

                            //Getting object
                            ho = property.Value.Getter(obj);

                            if (ho != null)
                            {
                                if (property.Value.TypeConverterToString != null)
                                {
                                    //Primitive type   
                                    WriteToString(sb, property.Value.Type, ho, property.Value.TypeConverterToString, ss);
                                }
                                else
                                {
                                    //Complex type                                   
                                    myAttributes = new StringBuilder();
                                    internalObject = SerializeObject(ho, myAttributes);
                                    if (myAttributes != null && myAttributes.Length > 0)
                                    {
                                        sb.Append(myAttributes.ToString());
                                        if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                            sb.Append(":");
                                    }

                                    //sb.Append("'");
                                    sb.Append(internalObject);
                                    //sb.Append("'");
                                }

                                //here ; can be skipped for short format
                                //if (!skipUnnecesarySymbols)

                                //sb.Append(";");
                            }
                            else
                            {
                                //here ; is NECESSARY, because of NULL value termination
                                sb.Append("null");
                            }
                        }
                        #endregion

                        sb.Append("}");

                        break;
                    case ObjectDefinition.eObjectDescription.Dictionary:

                        sb.Append("{");

                        #region "DICTIONARY"

                        foreach (var el in (IDictionary)obj)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(",");

                            key = ((DictionaryEntry)el).Key;
                            val = ((DictionaryEntry)el).Value;

                            //Writing key
                           // WriteToString(sb, od.GenericArgument1, key, od.keyTypeToStringConverter, ss);
                            WriteToString(sb, TypeString, Convert.ToString(key), null, ss);
                           

                            //here : can be skipped for short format for space economy
                            if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                sb.Append(":");

                            if (val != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object
                                    myAttributes = new StringBuilder();
                                    internalObject = SerializeObject(val, myAttributes);
                                    if (myAttributes != null && myAttributes.Length > 0)
                                    {
                                        sb.Append(myAttributes.ToString());
                                        if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                            sb.Append(":");
                                    }

                                    //sb.Append("'");
                                    sb.Append(internalObject);
                                   // sb.Append("'");
                                }
                                else
                                {
                                    WriteToString(sb, od.GenericArgument2, val, od.valueTypeToStringConverter, ss);
                                    //converterValue(sb, val);
                                }

                                //here ; can be skipped for short format
                                //if (!skipUnnecesarySymbols)
                                //sb.Append(";");
                            }
                            else
                            {
                                //here ; is NECESSARY, because of NULL value termination
                                sb.Append("null");
                            }
                        }

                        #endregion

                        sb.Append("}");

                        break;

                    case ObjectDefinition.eObjectDescription.List:

                        sb.Append("[");

                        #region "LIST"

                        foreach (var val1 in (IEnumerable)obj)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(",");

                            ////Empty key 
                            //sb.Append("'");
                            //sb.Append("'");

                            ////here : can be skipped for short format for space economy
                            //if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                            //    sb.Append(":");

                            if (val1 != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object
                                    myAttributes = new StringBuilder();
                                    internalObject = SerializeObject(val1, myAttributes);
                                    if (myAttributes != null && myAttributes.Length > 0)
                                    {
                                        sb.Append(myAttributes.ToString());
                                        if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                            sb.Append(":");
                                    }
                                    //sb.Append("'");
                                    sb.Append(internalObject);
                                    //sb.Append("'");
                                }
                                else
                                {
                                    WriteToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
                                }

                                //here ; can be skipped for short format
                                //if (!skipUnnecesarySymbols)
                               // sb.Append(";");
                            }
                            else
                            {
                                //here ; is NECESSARY, because of NULL value termination
                                sb.Append("null");
                            }
                        }

                        #endregion

                        sb.Append("]");

                        break;
                    case ObjectDefinition.eObjectDescription.HashSet:

                        sb.Append("[");

                        #region "HashSet"


                        foreach (var val1 in (IEnumerable)obj)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(",");


                            if (val1 != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object
                                    myAttributes = new StringBuilder();
                                    internalObject = SerializeObject(val1, myAttributes);
                                    if (myAttributes != null && myAttributes.Length > 0)
                                    {
                                        sb.Append(myAttributes.ToString());
                                        if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                            sb.Append(":");
                                    }

                                    //sb.Append("'");
                                    sb.Append(internalObject);
                                    //sb.Append("'");
                                }
                                else
                                {
                                    WriteToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
                                }

                                //here ; can be skipped for short format
                                //if (!skipUnnecesarySymbols)
                                //sb.Append(";");
                            }
                            else
                            {
                                //here ; is NECESSARY, because of NULL value termination
                                sb.Append("null");
                            }
                        }

                        #endregion

                        sb.Append("]");

                        break;
                    case ObjectDefinition.eObjectDescription.Array:

                        sb.Append("[");

                        #region ARRAY

                        //Filling attributes for the array
                        Array ar = (Array)obj;
                        //attribute.Append("[");
                        //for (int i = 0; i < ar.Rank; i++)
                        //{
                        //    attribute.Append(ar.GetUpperBound(i));
                        //    if (i != ar.Rank - 1)
                        //        attribute.Append(" ");
                        //}
                        //attribute.Append("]");

                        foreach (var val1 in ar)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(",");

                            ////Empty key 
                            //sb.Append("'");
                            //sb.Append("'");

                            ////here : can be skipped for short format for space economy
                            //if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                            //    sb.Append(":");

                            if (val1 != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object
                                    myAttributes = new StringBuilder();
                                    internalObject = SerializeObject(val1, myAttributes);
                                    if (myAttributes != null && myAttributes.Length > 0)
                                    {
                                        sb.Append(myAttributes.ToString());
                                        if (!this.ss.StringSerialization_SkipUnnecessarySmbols)
                                            sb.Append(":");
                                    }
                                    //sb.Append("'");
                                    sb.Append(internalObject);
                                    //sb.Append("'");
                                }
                                else
                                {
                                    WriteToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
                                }

                                //here ; can be skipped for short format
                                //if (!skipUnnecesarySymbols)
                                //sb.Append(";");
                            }
                            else
                            {
                                //here ; is NECESSARY, because of NULL value termination
                                sb.Append("null");
                            }
                        }

                        #endregion

                        sb.Append("]");

                        break;
                }

                

                return sb.ToString();
            }

            private static void WriteString(string s, StringBuilder _output, bool _useEscapedUnicode)
            { 
                int l = s.Length;
                for (var index = 0; index < l; ++index)
                {
                    var c = s[index];

                    switch (c)
                    {
                        case '\t': _output.Append("\\t"); break;
                        case '\r': _output.Append("\\r"); break;
                        case '\n': _output.Append("\\n"); break;
                        case '\b': _output.Append("\\b"); break;
                        case '\f': _output.Append("\\f"); break;
                        case '"':
                        case '\\': _output.Append('\\'); _output.Append(c); break;
                        default:
                            if (c >= 32 && c <= 126)
                                _output.Append(c);
                            else if (char.IsControl(c))
                            {
                                _output.Append("\\u");
                                _output.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo));
                            }
                            else
                                _output.Append(c);

                            break;
                    }
                }

                //if (runIndex != -1)
                //    _output.Append(s, runIndex, s.Length - runIndex);

                
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="sb"></param>
            /// <param name="t"></param>
            /// <param name="value"></param>
            /// <param name="?"></param>
            static void WriteToString(StringBuilder sb, Type t, object value, Action<StringBuilder, object> converter, SerializationSettings settings)
            {
                if (value == null)
                {
                    sb.Append("null");
                    return;
                }
                //int tc = (int)Type.GetTypeCode(t);
                             
                if (value is int || value is long || value is double || value is decimal || value is float || value is byte || value is short || value is sbyte || value is ushort || value is uint || value is ulong )
                //if (t.IsPrimitive && t!=typeof(char) && t!=typeof(bool))
                {
                    sb.Append(((IConvertible)value).ToString(NumberFormatInfo.InvariantInfo));
                }
                else if (value is DateTime)        //Very specific converter, moved out from the others
                {
                     var dt = (DateTime)value;

                    sb.Append("\"");
                    sb.Append(dt.Ticks);
                    //if (dt.Kind == DateTimeKind.Utc)
                    //    sb.Append(dt.ToString("yyyy-MM-ddTHH:mm:ss.msZ"));
                    //else
                    //    sb.Append(dt.ToString("yyyy-MM-ddTHH:mm:ss.ms"));
                    sb.Append("\"");
                }
                else if (value is byte[])
                {
                    var bt = (byte[])value;
                    sb.Append("\"");
                    sb.Append(Convert.ToBase64String(bt, 0, bt.Length, Base64FormattingOptions.None));
                    sb.Append("\"");
                }
                else if (value is string)
                {                 //https://github.com/ServiceStack/ServiceStack.Text/blob/master/src/ServiceStack.Text/Json/JsonUtils.cs
                    sb.Append("\"");
                    //sb.Append(value);
                    //string str = (string)value;
                    //var len = str.Length;
                    //for (var i = 0; i < len; i++)
                    //{
                    //    char c1 = str[i];
                    //    sb.Append(c1);
                    //}
                    
                    //var newStr = regex.Replace(str, m => map[m.Value]);
                    //sb.Append(newStr);

                    WriteString((string)value, sb, false);

                    sb.Append("\"");
                }
                else if (value is char)
                {
                    sb.Append("\"");
                    WriteString(Convert.ToString(value), sb, false);
                    sb.Append("\"");
                }
                //else if (value is char)
                //{
                //    var c = (char)value;
                //    sb.Append("\"");
                //    switch (c)
                //    {
                //        case '\t': sb.Append("\\t"); break;
                //        case '\r': sb.Append("\\r"); break;
                //        case '\n': sb.Append("\\n"); break;
                //        case '\b': sb.Append("\\b"); break;
                //        case '\f': sb.Append("\\f"); break;
                //        case '"':
                //        case '\\': sb.Append('\\'); sb.Append(c); break;
                //        default:
                //            sb.Append(c);
                //            //if (_useEscapedUnicode)
                //            //{
                //            //    sb.Append("\\u");
                //            //    sb.Append(((int)c).ToString("X4", NumberFormatInfo.InvariantInfo));
                //            //}
                //            //else
                //            //    sb.Append(c);

            //            break;
                //    }
                //    sb.Append("\"");
                //}
                else
                {

                    if (converter == null)
                    {
                        //Writing as it is probably Dictionary key
                        sb.Append("\"");
                        sb.Append(value);
                        sb.Append("\"");
                    }
                    else
                    {
                        // sb.Append("'");
                        converter(sb, value);
                        // sb.Append("'");
                    }
                }
            }

        }//eoc InternalJSONSerializer










        /// <summary>
        /// InternalJsonDeserializer currently
        /// </summary>
        internal class InternalJsonDeserializer
        {
            string _json = "";

            public object Deserialize(Type type, string str)
            //public Dictionary<string, string> Deserialize(string str)
            {
                _json = str;
                return Start(0);

            }

            enum eState
            {
                BEGIN,
                KEY,
                VALUE,
                KEY_END,
                ARRAY,
                ARRAY_STRING_ELEMENT,
                ARRAY_OBJECT_ELEMENT,
            }


            Dictionary<string, string> Start(int startFrom)
            {
                eState state = eState.BEGIN;
                char c;
                StringBuilder sbKey = new StringBuilder();
                StringBuilder sbValue = new StringBuilder();
                bool useNextSymbolAsChar = false;
                bool valueHasStarted = false;
                bool objectHasStarted = false;
                bool arrayHasStarted = false;
                int openBrackets = 0; //{ or [
                int openBrackets1 = 0; //{
                Dictionary<string, string> rt = new Dictionary<string, string>();

                Action Flush = () =>
                {
                    if (sbKey.Length > 0)
                        rt[sbKey.ToString()] = (sbValue.Length > 0) ? sbValue.ToString() : String.Empty;
                    sbKey.Clear();
                    sbValue.Clear();
                };


                Action FlushArray = () =>
                {
                    if (sbKey.Length > 0)
                        rt[sbKey.ToString()] = String.Empty;

                    sbKey.Clear();
                };

                char delimiter = ',';    //----------DONT CHANGE THIS VALUE, IT HAS AUTODISCOVERY----------
                char groupDelimiter = ':';
                char framer = '"';  //----------DONT CHANGE THIS VALUE, IT HAS AUTODISCOVERY----------

                for (int i = startFrom; i < _json.Length; i++)
                {
                    c = _json[i];
                    switch (state)
                    {
                        case eState.BEGIN:

                            if (c == framer || c == '\'')    // " or ' (auto discovery)
                            {
                                framer = c;
                                state = eState.KEY;
                                continue;
                            }
                            else if (c == '{' || c == ' ' || c == '\r' || c == '\n' || c == '\t') //,
                                continue;
                            else if (c == delimiter || c == ';') // , or ;
                            {
                                delimiter = c;
                                continue;
                            }
                            else if (c == '}')
                                return rt;     //Finish survey
                            else if (c == '[')
                            {
                                state = eState.ARRAY;
                                openBrackets += 1;
                                continue;
                            }
                            else
                                throw new Exception("Serna.dll. DeserializeJSON->WRONG START");

                        case eState.KEY:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }
                            if (c == framer)                //"                
                            {
                                state = eState.KEY_END;
                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                            {
                                sbKey.Append(c);
                            }
                            continue;

                        case eState.KEY_END:    //Must come value or next key or end of object
                            if (c == groupDelimiter)    //":"
                                state = eState.VALUE;
                            else if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                continue;
                            else if (c == delimiter)
                            {
                                delimiter = c;
                                Flush();
                                state = eState.BEGIN;     //Getting next key                  
                            }
                            else if (c == '}')
                            {
                                Flush();
                                return rt;
                            }
                            else if (c == ';')  //Changing delimiter
                            {
                                delimiter = c;
                                Flush();
                                state = eState.BEGIN;     //Getting next key                  
                            }
                            else
                                throw new Exception("Serna.dll. DeserializeJSON->WRONG KEY_END");

                            continue;

                        case eState.VALUE:
                            if (useNextSymbolAsChar)
                            {
                                sbValue.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (valueHasStarted)
                            {
                                if (c == framer)    //"
                                {
                                    Flush();
                                    valueHasStarted = false;
                                    state = eState.BEGIN;
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else if (arrayHasStarted)
                            {
                                if (c == '[')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                }
                                else if (c == ']')
                                {
                                    if ((openBrackets - 1) == 0)
                                    {
                                        arrayHasStarted = false;
                                        state = eState.BEGIN;
                                        openBrackets = 0;
                                        sbValue.Append(c);
                                        Flush();
                                    }
                                    else
                                    {
                                        sbValue.Append(c);
                                        openBrackets--;
                                    }
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else if (objectHasStarted)
                            {
                                if (c == '{')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                }
                                else if (c == '}')
                                {
                                    if ((openBrackets - 1) == 0)
                                    {
                                        objectHasStarted = false;
                                        state = eState.BEGIN;
                                        openBrackets = 0;
                                        sbValue.Append(c);
                                        Flush();
                                    }
                                    else
                                    {
                                        sbValue.Append(c);
                                        openBrackets--;
                                    }
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else
                            {
                                //Ignoring all garbage
                                if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                    continue;
                                else if (c == delimiter)    //,
                                {
                                    Flush();
                                    state = eState.BEGIN;
                                }
                                else if (c == framer)   //framer
                                    valueHasStarted = true;
                                else if (c == '{')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                    objectHasStarted = true;
                                }
                                else if (c == '[')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                    arrayHasStarted = true;
                                }
                                else if (c == '}')
                                {
                                    Flush();
                                    return rt;
                                }
                                else
                                    sbValue.Append(c);  //can be digit without brackets
                            }


                            continue;
                        case eState.ARRAY:

                            if (c == '[')
                            {
                                sbKey.Append(c);
                                openBrackets += 1;
                            }
                            else if (c == ']')
                            {
                                if ((openBrackets - 1) == 0)
                                {
                                    Flush();
                                    return rt;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                    openBrackets--;
                                }
                            }
                            if (c == '{')
                            {
                                openBrackets1 = 1;
                                state = eState.ARRAY_OBJECT_ELEMENT;
                                sbKey.Append(c);
                                continue;
                            }
                            if (c == '"')
                            {
                                state = eState.ARRAY_STRING_ELEMENT;
                                continue;
                            }
                            //skipping the rest of symbols

                            continue;
                        case eState.ARRAY_STRING_ELEMENT:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (c == '"')
                            {
                                FlushArray();
                                state = eState.ARRAY;
                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                                sbKey.Append(c);

                            continue;
                        case eState.ARRAY_OBJECT_ELEMENT:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (c == '{')
                            {
                                sbKey.Append(c);
                                openBrackets1 += 1;
                            }
                            else if (c == '}')
                            {
                                if ((openBrackets1 - 1) == 0)
                                {
                                    sbKey.Append(c);
                                    openBrackets1 = 0;
                                    FlushArray();
                                    state = eState.ARRAY;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                    openBrackets1--;
                                }

                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                                sbKey.Append(c);

                            continue;
                    }
                }

                Flush();
                return rt;
            }//eo Start


        }//eoc





        /// <summary>
        /// InternalJsonDeserializer currently to dictionary
        /// </summary>
        internal class InternalJsonDeserializerToDictionary
        {
            string _json = "";

            public Dictionary<string, string> Deserialize(string str)
            {
                _json = str;
                return Start(0);

            }

            enum eState
            {
                BEGIN,
                KEY,
                VALUE,
                KEY_END,
                ARRAY,
                ARRAY_STRING_ELEMENT,
                ARRAY_OBJECT_ELEMENT,
            }


            Dictionary<string, string> Start(int startFrom)
            {
                eState state = eState.BEGIN;
                char c;
                StringBuilder sbKey = new StringBuilder();
                StringBuilder sbValue = new StringBuilder();
                bool useNextSymbolAsChar = false;
                bool valueHasStarted = false;
                bool objectHasStarted = false;
                bool arrayHasStarted = false;
                int openBrackets = 0; //{ or [
                int openBrackets1 = 0; //{
                Dictionary<string, string> rt = new Dictionary<string, string>();

                Action Flush = () =>
                {
                    if (sbKey.Length > 0)
                        rt[sbKey.ToString()] = (sbValue.Length > 0) ? sbValue.ToString() : String.Empty;
                    sbKey.Clear();
                    sbValue.Clear();
                };


                Action FlushArray = () =>
                {
                    if (sbKey.Length > 0)
                        rt[sbKey.ToString()] = String.Empty;

                    sbKey.Clear();
                };

                char delimiter = ',';    //----------DONT CHANGE THIS VALUE, IT HAS AUTODISCOVERY----------
                char groupDelimiter = ':';
                char framer = '"';  //----------DONT CHANGE THIS VALUE, IT HAS AUTODISCOVERY----------

                for (int i = startFrom; i < _json.Length; i++)
                {
                    c = _json[i];
                    switch (state)
                    {
                        case eState.BEGIN:

                            if (c == framer || c == '\'')    // " or ' (auto discovery)
                            {
                                framer = c;
                                state = eState.KEY;
                                continue;
                            }
                            else if (c == '{' || c == ' ' || c == '\r' || c == '\n' || c == '\t') //,
                                continue;
                            else if (c == delimiter || c == ';') // , or ;
                            {
                                delimiter = c;
                                continue;
                            }
                            else if (c == '}')
                                return rt;     //Finish survey
                            else if (c == '[')
                            {
                                state = eState.ARRAY;
                                openBrackets += 1;
                                continue;
                            }
                            else
                                throw new Exception("Serna.dll. DeserializeJSON->WRONG START");

                        case eState.KEY:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }
                            if (c == framer)                //"                
                            {
                                state = eState.KEY_END;
                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                            {
                                sbKey.Append(c);
                            }
                            continue;

                        case eState.KEY_END:    //Must come value or next key or end of object
                            if (c == groupDelimiter)    //":"
                                state = eState.VALUE;
                            else if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                continue;
                            else if (c == delimiter)
                            {
                                delimiter = c;
                                Flush();
                                state = eState.BEGIN;     //Getting next key                  
                            }
                            else if (c == '}')
                            {
                                Flush();
                                return rt;
                            }
                            else if (c == ';')  //Changing delimiter
                            {
                                delimiter = c;
                                Flush();
                                state = eState.BEGIN;     //Getting next key                  
                            }
                            else
                                throw new Exception("Serna.dll. DeserializeJSON->WRONG KEY_END");

                            continue;

                        case eState.VALUE:
                            if (useNextSymbolAsChar)
                            {
                                sbValue.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (valueHasStarted)
                            {
                                if (c == framer)    //"
                                {
                                    Flush();
                                    valueHasStarted = false;
                                    state = eState.BEGIN;
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else if (arrayHasStarted)
                            {
                                if (c == '[')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                }
                                else if (c == ']')
                                {
                                    if ((openBrackets - 1) == 0)
                                    {
                                        arrayHasStarted = false;
                                        state = eState.BEGIN;
                                        openBrackets = 0;
                                        sbValue.Append(c);
                                        Flush();
                                    }
                                    else
                                    {
                                        sbValue.Append(c);
                                        openBrackets--;
                                    }
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else if (objectHasStarted)
                            {
                                if (c == '{')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                }
                                else if (c == '}')
                                {
                                    if ((openBrackets - 1) == 0)
                                    {
                                        objectHasStarted = false;
                                        state = eState.BEGIN;
                                        openBrackets = 0;
                                        sbValue.Append(c);
                                        Flush();
                                    }
                                    else
                                    {
                                        sbValue.Append(c);
                                        openBrackets--;
                                    }
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else
                            {
                                //Ignoring all garbage
                                if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                    continue;
                                else if (c == delimiter)    //,
                                {
                                    Flush();
                                    state = eState.BEGIN;
                                }
                                else if (c == framer)   //framer
                                    valueHasStarted = true;
                                else if (c == '{')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                    objectHasStarted = true;
                                }
                                else if (c == '[')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                    arrayHasStarted = true;
                                }
                                else if (c == '}')
                                {
                                    Flush();
                                    return rt;
                                }
                                else
                                    sbValue.Append(c);  //can be digit without brackets
                            }


                            continue;
                        case eState.ARRAY:

                            if (c == '[')
                            {
                                sbKey.Append(c);
                                openBrackets += 1;
                            }
                            else if (c == ']')
                            {
                                if ((openBrackets - 1) == 0)
                                {
                                    Flush();
                                    return rt;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                    openBrackets--;
                                }
                            }
                            if (c == '{')
                            {
                                openBrackets1 = 1;
                                state = eState.ARRAY_OBJECT_ELEMENT;
                                sbKey.Append(c);
                                continue;
                            }
                            if (c == '"')
                            {
                                state = eState.ARRAY_STRING_ELEMENT;
                                continue;
                            }
                            //skipping the rest of symbols

                            continue;
                        case eState.ARRAY_STRING_ELEMENT:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (c == '"')
                            {
                                FlushArray();
                                state = eState.ARRAY;
                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                                sbKey.Append(c);

                            continue;
                        case eState.ARRAY_OBJECT_ELEMENT:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (c == '{')
                            {
                                sbKey.Append(c);
                                openBrackets1 += 1;
                            }
                            else if (c == '}')
                            {
                                if ((openBrackets1 - 1) == 0)
                                {
                                    sbKey.Append(c);
                                    openBrackets1 = 0;
                                    FlushArray();
                                    state = eState.ARRAY;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                    openBrackets1--;
                                }

                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                                sbKey.Append(c);

                            continue;
                    }
                }

                Flush();
                return rt;
            }//eo Start


        }//eoc
    }
}
